#include "../common/dispatcher.hpp"
#include "requestor.hpp"
#include "rpc_caller.hpp"
#include "rpc_registry.hpp"
// #include "rpc_topic.hpp"

namespace myrpc 
{
    namespace client 
    {
        class RegistryClient 
        {
        public:
            using ptr = std::shared_ptr<RegistryClient>;
            //构造函数传入 注册中心 的地址信息，用于连接注册中心
            RegistryClient(const std::string &ip, int port)
                : _requestor(std::make_shared<Requestor>())
                , _provider(std::make_shared<client::Provider>(_requestor))
                , _dispatcher(std::make_shared<Dispatcher>()) 
            {
                auto rsp_cb = std::bind(&client::Requestor::onResponse, _requestor.get(), 
                                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MType::RSP_SERVICE, rsp_cb);

                auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(), 
                                                        std::placeholders::_1, std::placeholders::_2);
                _client = ClientFactory::create(ip, port);
                _client->setMessageCallback(message_cb);
                _client->connect();
            }
            //向外提供的服务注册接口
            bool registryMethod(const std::string &method, const Address &host) 
            {
                return _provider->registryMethod(_client->connection(), method, host);
            }
        private:
            Requestor::ptr _requestor;          // 请求管理模块
            client::Provider::ptr _provider;    // 服务提供者
            Dispatcher::ptr _dispatcher;
            BaseClient::ptr _client;
        };

        class DiscoveryClient 
        {
        public:
            using ptr = std::shared_ptr<DiscoveryClient>;
            //构造函数传入注册中心的地址信息，用于连接注册中心
            DiscoveryClient(const std::string &ip, int port, const Discoverer::OfflineCallback &cb)
                : _requestor(std::make_shared<Requestor>())
                , _discoverer(std::make_shared<client::Discoverer>(_requestor, cb))
                , _dispatcher(std::make_shared<Dispatcher>())
            {
                auto rsp_cb = std::bind(&client::Requestor::onResponse, _requestor.get(), 
                                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MType::RSP_SERVICE, rsp_cb);

                auto req_cb = std::bind(&client::Discoverer::onServiceRequest, _discoverer.get(),
                                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<ServiceRequest>(MType::REQ_SERVICE, req_cb);

                auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(), 
                                                        std::placeholders::_1, std::placeholders::_2);
                _client = ClientFactory::create(ip, port);
                _client->setMessageCallback(message_cb);
                _client->connect();
            }
            //向外提供的服务发现接口
            bool serviceDiscovery(const std::string &method, Address &host) 
            {
                return _discoverer->serviceDiscovery(_client->connection(), method, host);
            }
        private:
            Requestor::ptr _requestor;              
            client::Discoverer::ptr _discoverer;
            Dispatcher::ptr _dispatcher;
            BaseClient::ptr _client;
        };
        
        // RpcClient包含两个功能提供基础的rpc客户端搭建 和 服务发现功能的rpc客户端
        class RpcClient 
        {
        public:
            using ptr = std::shared_ptr<RpcClient>;
            //enableDiscovery--表示是否启用服务发现功能，也决定了传入的地址信息是注册中心的地址，还是服务提供者的地址
            // 启用服务发现功能 就是传入的地址信息是注册中心的地址  该客户端就是一个服务发现客户端
            RpcClient(bool enableDiscovery, const std::string &ip, int port)
                : _enableDiscovery(enableDiscovery)
                , _requestor(std::make_shared<Requestor>())
                , _dispatcher(std::make_shared<Dispatcher>())
                , _caller(std::make_shared<myrpc::client::RpcCaller>(_requestor)) 
            {
                //针对rpc请求后的响应进行的回调处理
                auto rsp_cb = std::bind(&client::Requestor::onResponse, _requestor.get(), 
                                                                std::placeholders::_1, std::placeholders::_2);
                // dispatcher 是 对收到的消息 进行回调处理
                _dispatcher->registerHandler<BaseMessage>(MType::RSP_RPC, rsp_cb);

                //如果启用了服务发现，地址信息是注册中心的地址，是服务发现客户端需要连接的地址，则通过地址信息实例化discovery_client
                //如果没有启用服务发现，则地址信息是服务提供者的地址，则直接实例化好rpc_client
                if (_enableDiscovery)   // 启用了服务发现
                {
                    auto offline_cb = std::bind(&RpcClient::delClient, this, std::placeholders::_1);
                    _discovery_client = std::make_shared<DiscoveryClient>(ip, port, offline_cb);
                }
                else 
                {
                    auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(), 
                                                                std::placeholders::_1, std::placeholders::_2);
                    _rpc_client = ClientFactory::create(ip, port);  // 此时的ip和port就是服务提供者的
                    _rpc_client->setMessageCallback(message_cb);
                    _rpc_client->connect();
                }
            }
            // RPC的三种调用
            bool call(const std::string &method, const Json::Value &params, Json::Value &result) 
            {
                //获取服务提供者：1. 服务发现；  2. 固定服务提供者(provide)（若没有启用服务发现，则使用该方法）
                BaseClient::ptr client = getClient(method);
                if (client.get() == nullptr) 
                {
                    return false;
                }
                //3. 通过客户端连接，发送rpc请求
                return _caller->call(client->connection(), method, params, result);
            }
            bool call(const std::string &method, const Json::Value &params, RpcCaller::JsonAsyncResponse &result) 
            {
                BaseClient::ptr client = getClient(method);
                if (client.get() == nullptr) 
                {
                    return false;
                }
                //3. 通过客户端连接，发送rpc请求
                return _caller->call(client->connection(), method, params, result);
            }
            bool call(const std::string &method, const Json::Value &params, const RpcCaller::JsonResponseCallback &cb) 
            {
                BaseClient::ptr client = getClient(method);
                if (client.get() == nullptr) 
                {
                    return false;
                }
                //3. 通过客户端连接，发送rpc请求
                return _caller->call(client->connection(), method, params, cb);
            }
        private:
            
            BaseClient::ptr newClient(const Address &host) 
            {
                auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(), 
                                                    std::placeholders::_1, std::placeholders::_2);
                auto client = ClientFactory::create(host.first, host.second);
                client->setMessageCallback(message_cb);
                client->connect();
                putClient(host, client);    // 添加映射关系
                return client;
            }
            // 获取一个主机(provier)的客户端
            BaseClient::ptr getClient(const Address &host) 
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _rpc_clients.find(host);
                if (it == _rpc_clients.end()) 
                {
                    return BaseClient::ptr();
                }
                return it->second;
            }
            BaseClient::ptr getClient(const std::string method) 
            {
                BaseClient::ptr client;
                if (_enableDiscovery) 
                {
                    //1. 通过服务发现，获取服务提供者地址信息
                    Address host;
                    bool ret = _discovery_client->serviceDiscovery(method, host);
                    if (ret == false) 
                    {
                        ELOG("当前 %s 服务，没有找到服务提供者！", method.c_str());
                        return BaseClient::ptr();
                    }
                    //2. 查看服务提供者是否已有实例化客户端，有则直接使用，没有则创建
                    client = getClient(host);
                    if (client.get() == nullptr) //没有找打已实例化的客户端，则创建
                    {
                        client = newClient(host);
                    }
                }
                else 
                {
                    client = _rpc_client;
                }
                return client;
            }
            // 插入
            void putClient(const Address &host, BaseClient::ptr &client) 
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _rpc_clients.insert(std::make_pair(host, client));
            }
            void delClient(const Address &host) 
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _rpc_clients.erase(host);
            }
        private:
            // 仿函数
            struct AddressHash 
            {
                size_t operator()(const Address &host) const
                {
                    std::string addr = host.first + std::to_string(host.second);
                    return std::hash<std::string>{}(addr);
                }
            };
            bool _enableDiscovery;      // 是否启用服务发现功能
            DiscoveryClient::ptr _discovery_client;
            Requestor::ptr _requestor;
            RpcCaller::ptr _caller;
            Dispatcher::ptr _dispatcher;
            BaseClient::ptr _rpc_client;// 用于未启用服务发现
            std::mutex _mutex;
            // 服务提供者的地址    rpc_client
            // Address自定义类型，编译器不知道怎么计算hash值，需要用到仿函数告诉编译器怎么计算
            //             <"127.0.0.1:8080"(provide), client1>
            std::unordered_map<Address, BaseClient::ptr, AddressHash> _rpc_clients;// 用于服务发现的客户端连接池
        };
    }
}